package com.fsh.auth.domain.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.fsh.auth.common.enums.AuthUserStatusEnum;
import com.fsh.auth.common.enums.IsDeletedFlagEnum;
import com.fsh.auth.domain.constants.AuthConstant;
import com.fsh.auth.domain.convert.AuthUserBOConverter;
import com.fsh.auth.domain.entity.AuthUserBO;
import com.fsh.auth.domain.redis.RedisUtil;
import com.fsh.auth.domain.service.AuthUserDomainService;
import com.fsh.auth.infra.basic.entity.*;
import com.fsh.auth.infra.basic.service.*;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AuthUserDomainServiceImpl implements AuthUserDomainService {

    @Resource
    private AuthUserService authUserService;
    @Resource
    private AuthRoleService authRoleService;
    @Resource
    private AuthUserRoleService authUserRoleService;
    @Resource
    private AuthRolePermissionService authRolePermissionService;
    @Resource
    private AuthPermissionService authPermissionService;

    /**
     * 盐
     */
    private String salt = "fsh-club";

    @Resource
    private RedisUtil redisUtil;

    private String authPermissionPrefix = "auth.permission";

    private String authRolePrefix = "auth.role";

    private static final String LOGIN_CODE = "loginCode";


    /**
     * 用户  扫描二维码和验证码 登录
     *
     * @param validCode
     */
    @Override
    public SaTokenInfo doLogin(String validCode) {
        //构造 redis 的 key , 获取 openid
        String codeKey = redisUtil.buildKey(LOGIN_CODE, validCode);
        String openId = redisUtil.get(codeKey);
        if(log.isInfoEnabled()){
            log.info("AuthUserDomainServiceImpl.class-doLogin()-openId:{}",openId);
        }
        if (StringUtils.isBlank(openId)) {
            return null;
        }
        //注册用户。  username 就是 openId
        AuthUserBO authUserBO = new AuthUserBO();
        authUserBO.setUserName(openId);
        Boolean flag = this.register(authUserBO);

        //satoken 登录 该用户 openid(username)
        if (flag) {
            StpUtil.login(openId);
        }
        return StpUtil.getTokenInfo();
    }

    /**
     * 注册
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(AuthUserBO authUserBO) {

        // 已经注册过的用户，不能再重新注册 。
        AuthUser authUser1 = authUserService.queryByUsername(authUserBO.getUserName());
        if (authUser1 != null) {
            //表示数据库中有
            return true;
        }

        //数据库中不存在这个 openid ，那就要注册了。

        AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBOToEntity(authUserBO);
        //密码加盐 。  这里之所以判空是因为：首次扫描二维码验证码登录，调用这个注册方法，是没有密码的。
        if (StringUtils.isNotBlank(authUser.getPassword())) {
            authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(), salt));
        }
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());

        Integer count = authUserService.insert(authUser);

        // TODO 这里应该建立一个初步的角色关联  。 还要放到redis缓存中
        AuthRole authRole = new AuthRole();
        authRole.setRoleKey(AuthConstant.NORMAL_USER);//查询这个角色的id ，和用户关联起来。
        AuthRole role = authRoleService.queryByCondition(authRole);

        //构建 AuthUserRole ，插入 用户角色表
        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setUserId(authUser.getId());
        authUserRole.setRoleId(role.getId());
        authUserRole.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());

        //插入数据库
        Integer count2 = authUserRoleService.insert(authUserRole);
        //构建缓存
        initAuthAndRole(authRole,authUser);

        return (count + count2) > 1;
    }

    /**
     * 缓存中存入 角色和权限
     */
    private void initAuthAndRole(AuthRole authRole,AuthUser authUser){
        //构建缓存的key : prefix + username  （username就是loginId ，就是扫码登录的openid）
        String roleKey = redisUtil.buildKey(authRolePrefix, authUser.getUserName());
        //将初步建立的 用户角色关联表放到redis缓存中
        List<AuthRole> authRoleList = new ArrayList<>();
        authRoleList.add(authRole);
        redisUtil.set(roleKey, new Gson().toJson(authRoleList));

        //把 该初步角色 的 权限查出来
        AuthRolePermission authRolePermission = new AuthRolePermission();
        authRolePermission.setRoleId(authRole.getId());
        List<AuthRolePermission> authRolePermissions = authRolePermissionService.queryByCondition(authRolePermission);
        //将权限id 收集到集合中 , 得到权限集合id
        List<Long> permissionIds = authRolePermissions.stream().map(AuthRolePermission::getId).collect(Collectors.toList());

        //根据权限id集合 查询出 权限集合
        List<AuthPermission> authPermissionList = authPermissionService.queryByIdList(permissionIds);

        //构建缓存key = prefix + username
        String permissionKey = redisUtil.buildKey(authPermissionPrefix, authUser.getUserName());
        //将用户初步 关联的角色的 权限 放到缓存中
        redisUtil.set(permissionKey, new Gson().toJson(authPermissionList));
    }

    /**
     * 获取用户信息 ,  用来回显
     */
    @Override
    public AuthUserBO getUserInfo(AuthUserBO authUserBO) {
        AuthUser authUser=new AuthUser();
        authUser.setUserName(authUserBO.getUserName());
        //这里可以使用List集合来接收，以防万一数据库真的插入了多条一模一样的userName。
        // 但是我们已经对数据库的user_name做了唯一索引，所以有且只有一条。
        AuthUser authUser1=authUserService.queryByCondition(authUser);
        return AuthUserBOConverter.INSTANCE.convertEntityToBo(authUser1);
    }

    /**
     * 更新用户信息
     */
    @Override
    public Boolean update(AuthUserBO authUserBO) {
        AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBOToEntity(authUserBO);
//        authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(),salt));
        Integer ccount = authUserService.update(authUser);
        // TODO 有任何的更新，都要与缓存进行同步的修改
        return ccount > 0;
    }

    /**
     * 逻辑删除用户
     */
    @Override
    public Boolean delete(AuthUserBO authUserBO) {
        AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBOToEntity(authUserBO);
        authUser.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode());
        Integer count = authUserService.update(authUser);
        // TODO 有任何的更新，都要与缓存进行同步的修改
        return count > 0;
    }
}
